import { hasOwn, isObject } from '../../shared/util'
import { hasProto } from '../util/env'
import { def } from '../util/lang'
import { arrayMethods } from './array'

const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
// observe 响应式入口 
export function observe(value,asRootData) {
    // || value instanceof VNode
    if (!isObject(value)) {
        return
    }
    let ob
    ob = new Observer(value)
    // todo 是否存在__ob__属性 有则直接返回
    // todo 关于asRootData ob.vmCount
    return ob
}
// Observer类
export class Observer {
    constructor(value){
        this.value = value
        // todo dep
        // def(value,'__ob__',this)
        // 响应数据
        // todo 如果是数组
        if (Array.isArray(value)) {
            if(hasProto){
                protoArgument(value, arrayMethods)
            }else {
                copyArgument(value, arrayMethods, arrayKeys)
            }
            this.observeArray(value)
        }else {
            this.walk(value)
        }
    }
    // 遍历执行 defineReactive
    walk(obj) {
        const keys = Object.keys(obj)
        for(let i = 0; i < keys.length; i++){
            defineReactive(obj,keys[i],obj[keys[i]])
        }
    }
    observeArray(items){
        for(let i = 0, l = items.length; i < l; i++ ){
            observe(items[i])
        }
    }
}

// defineReactive
export function defineReactive(obj, key, val, customSetter, shallow) {
    // todo Dep
    // 各种错误修正
    const property = Object.getOwnPropertyDescriptor(obj, key)
    if(property && property.configurable === false) {
        return
    }
    const getter = property && property.get
    const setter = property && property.set
    if((!getter || setter) && arguments.length === 2) {
        val = obj[key]
    }
    let childOb = !shallow && observe(val)

    Object.defineProperty(obj, key, {
        enumerable:true,
        configurable:true,
        get:function reactiveGetter () {
            const value = getter ? getter.call(obj) : val
            // todo dep
            return value
        },
        set:function reactiveSetter(newVal) {
            const value = getter ? getter.call(obj) : val
            if (newVal === value || (newVal !== newVal && value !== value)) {
                return
            }
            if (setter) {
                setter.call(obj, newVal)
            } else {
                val = newVal // 如果值变化 则更新值
            }
            childOb = !shallow && observe(newVal) // 递归更新子对象
            // todo 优化 递归 通知
        }
    })
}

export function protoArgument (target, src) {
    target.__proto__ = src
}

export function copyArgument (target, src, keys) {
    for(let i = 0, l = keys.length; i < l; i++ ){
        const key = keys[i]
        def(target, key, src[key])
    }
}